#pragma once
#include "PDTNode.h"
#include "PDTUnaryOp.h"
#include "PDTVar.h"

class PDTExp: public PDTNode {
protected:
	friend class PDTUnresolvedExp;
	PDTExp() {
	}
	PDTUnaryOp * unaryOperator;
public:
	PDTExp(PDTCodeGraph & CG);
	virtual NodeTypeEnum getType()=0;//{return PDTExp_Em;}

	//Temporary function which search for a variable in the graph having a certain name
	virtual PDTVar * searchForVariable(string name) {
		for (unsigned int i = 0; i < children.size(); i++) {
			if (children[i]->isSequenceAccessNum()
					|| children[i]->isSequenceAccessBool()) //If we're accessing a sequence,
			{
				if (children[i]->getChildren()[1]->isVar()) //If children is Var having the needed name, return it
				{
					PDTVar
							*variable =
									dynamic_cast<PDTVar*> (children[i]->getChildren()[1]);
					string s = variable->getName();
					if (s.substr(s.size() - name.size(), name.size()) == name) {
						return variable;
					}
				} else //else, recursive call
				{
					PDTVar
							* toReturn =
									(dynamic_cast<PDTExp*> (children[i]->getChildren()[1]))->searchForVariable(
											name);
					if (toReturn != NULL) {
						return toReturn;
					}
				}
			} else {
				if (children[i]->isVar()) //Simply a variable, return it
				{
					PDTVar *variable = dynamic_cast<PDTVar*> (children[i]);
					string s = variable->getName();
					if (s.substr(s.size() - name.size(), name.size()) == name)
						return variable;
				}

				if (children[i]->isExp()) //If have an expression, recursive call
				{
					PDTVar
							* toReturn =
									(dynamic_cast<PDTExp*> (children[i]))->searchForVariable(
											name);
					if (toReturn != NULL)
						return toReturn;
				}
			}
		}
		cout << "Variable Not Found" << endl;
		return NULL;
	}

	virtual bool isExp() {
		return true;
	}
};

class PDTExpNum: public PDTExp {
public:
	PDTExpNum(PDTCodeGraph & CG);
	virtual bool isNum() {
		return true;
	}
	virtual NodeTypeEnum getType()=0;
	virtual bool isExpNum() {
		return true;
	}
};

class PDTExpBool: public PDTExpNum {
public:
	PDTExpBool(PDTCodeGraph & CG);
	virtual bool isBool() {
		return true;
	}
	virtual bool isNum() {
		return false;
	}
	virtual bool isExpNum() {
		return false;
	}
	virtual NodeTypeEnum getType() =0;
};

class PDTExpSet: public PDTExp {
public:
	PDTExpSet(PDTCodeGraph & CG);
	virtual bool isSet() {
		return true;
	}
	NodeTypeEnum getType() = 0;
};

class PDTUnresolvedExp: public PDTExp , public TraverseIfc <PDTUnresolvedExp, PDTNode> {

public:
 TRAVERSAL_IFC_IMPL(PDTUnresolvedExp)

private:
	PDTUnresolvedExp() {
	}
public:
	virtual stringstream & toString(stringstream& ss) const {
		ss << "(Unresolved Expression)";
		return ss;
	}
	NodeTypeEnum getType() {
		return PDTUnresolvedExp_Em;
	}

	static PDTUnresolvedExp unresolvedExp;
};

class PDTUnaryNumExpr: public PDTExp , public TraverseIfc <PDTUnaryNumExpr, PDTNode> {
public:
 TRAVERSAL_IFC_IMPL(PDTUnaryNumExpr)
protected:
	PDTUnaryOp * unaryOp;
public:
	PDTUnaryNumExpr(PDTCodeGraph & CG, PDTUnaryOp & unary,
			PDTExpNum & expression) :
		PDTExp(CG), unaryOp(&unary) {
		addChild(&expression);
		expression.addParent(this);
	}
	NodeTypeEnum getType() {
		return PDTUnaryNumExp_Em;
	}
    virtual bool isMinus() const {
        return unaryOp->isUnaryMinus();
    }
	virtual bool isUnaryNumExpr() {
		return true;
	}
	virtual bool eval(PDTCodeGraph &CG);

	virtual stringstream& toString(stringstream& ss) const {
		unaryOp->toString(ss);
		children[0]->toString(ss);
		return ss;
	}
};
